home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / archival / ftp / BFTP.312 / parsedate.y < prev    next >
Encoding:
Text File  |  1990-06-29  |  35.3 KB  |  1,505 lines

  1. /***********************************************************
  2.         Copyright IBM Corporation 1988
  3.  
  4.                       All Rights Reserved
  5.  
  6. Permission to use, copy, modify, and distribute this software and its 
  7. documentation for any purpose and without fee is hereby granted, 
  8. provided that the above copyright notice appear in all copies and that
  9. both that copyright notice and this permission notice appear in 
  10. supporting documentation, and that the name of IBM not be
  11. used in advertising or publicity pertaining to distribution of the
  12. software without specific, written prior permission.  
  13.  
  14. IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  15. ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  16. IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  17. ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  18. WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  19. ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  20. SOFTWARE.
  21. ******************************************************************/
  22. /* $Header: parsedate.y,v 1.3 88/09/15 18:54:25 ghoti Exp $ */
  23. /* $ACIS: $ */
  24. /* $Source: /afs/andrew.cmu.edu/itc/src/andrew/ams/libs/ms/RCS/parsedate.y,v $ */
  25.  
  26. /*
  27.  *  parsedate - parse date specification. Originally pdate.
  28.  *
  29.  */
  30.  
  31. %start date_time
  32. %token NUMBER NUMBER4 WEEKDAY MONTH HOUR SHOUR
  33. %token TODAY NOW TONIGHT NEXT THIS DAY WEEK FORTNIGHT
  34. %token EVERY FROM BEFORE THE A AT ON LAST AFTER IN OF AGO WORD_MONTH
  35. %token NOON AMPM TIMEKEY
  36. %token AND NWORD NTHWORD ST ND RD TH
  37. %token CHRISTMAS NEW YEAR
  38. %token JUNK
  39. %token ZONE
  40.  
  41. /* Resolve shift/reduce conflicts on THE */
  42.  
  43. %left THE
  44.  
  45. %{
  46. #include "andrewOS.h"  /* sys/types.h sys/time.h */
  47. #include <setjmp.h>
  48.  
  49. /* used to include c.h, however only dependency was CERROR */
  50.  
  51. #define CERROR (-1)
  52.  
  53. # define MAXPEEP 3
  54. # define NDTMS 10
  55. # define NOYEAR -1901
  56. /* PTM: indicates ptm */
  57. # define PTM -1
  58. /* CURRTM: indicates current time */
  59. # define CURRTM -2
  60. /* CURRDATE: indicates current date (time set to -1). */
  61. # define CURRDATE -3
  62. /* PAST, FUTURE: logical values for past and future */
  63. # define PAST 1
  64. # define FUTURE 0
  65.  
  66. static int junk_err;            /* junk token give error? */
  67. static int ppf;                /* past, or future */
  68. static struct tm scurrtm;
  69. static struct tm *currtm = &scurrtm;    /* current time */
  70. static char *strp;            /* current position in date string */
  71. static int delim;            /* previous field delimiter */
  72. static jmp_buf errbuf;            /* jump location for parse errors */
  73. extern char *nxtarg();
  74. extern long time();
  75. extern struct tm *localtime();
  76. struct stable { char *text;  int token;  int lexval; };
  77. static int shour, tmkey;
  78. static int zone;    /* Offset in minutes from GMT specified */
  79. static struct tm ptm;
  80. static int pcount;
  81. static int result;
  82. static char *unkstring = "unknown error";
  83.  
  84. /* Date structure for constraining dates */
  85. struct dtm 
  86. { int repn;                      /* Current representation */
  87.   int days;
  88.   struct tm tm;
  89.   int count;
  90. };
  91.  
  92. static struct dtm dtm[NDTMS];
  93. static int dtmused[NDTMS];
  94.  
  95. /* Representations */
  96. # define RDAYS 0
  97. # define RTM 1
  98.  
  99. /*
  100.  *  Month and week day names (upper case only) and other words.
  101.  */
  102. struct stable strings[] =
  103. {
  104.   { "JAN*UARY", MONTH, 0 },        /* months (0-11) */
  105.   { "FEB*RUARY", MONTH, 1 },
  106.   { "MAR*CH", MONTH, 2 },
  107.   { "APR*IL", MONTH, 3 },
  108.   { "MAY", MONTH, 4 },
  109.   { "JUN*E", MONTH, 5 },
  110.   { "JUL*Y", MONTH, 6 },
  111.   { "AUG*UST", MONTH, 7 },
  112.   { "SEP*TEMBER", MONTH, 8 },
  113.   { "OCT*OBER", MONTH, 9 },
  114.   { "NOV*EMBER", MONTH, 10 },
  115.   { "DEC*EMBER", MONTH, 11 },
  116.   { "SUN*DAY", WEEKDAY, 0 },    /* days of the week (0-6) */
  117.   { "MON*DAY", WEEKDAY, 1 },
  118.   { "TUE*SDAY", WEEKDAY, 2 },
  119.   { "WED*NESDAY", WEEKDAY, 3 },
  120.   { "THU*RSDAY", WEEKDAY, 4 },
  121.   { "FRI*DAY", WEEKDAY, 5 },
  122.   { "SAT*URDAY", WEEKDAY, 6 },
  123.   { "YESTERDAY", TODAY, -1 },    /* relative to today */
  124.   { "TODAY", TODAY, 0 },
  125.   { "TONIGHT", TONIGHT, 0 },
  126.   { "NOW", NOW, 0 },
  127.   { "AGO", AGO, 0 },
  128.   { "TOMORROW", TODAY, 1 },
  129.   { "NEXT", NEXT, 0 },        /* keywords */
  130.   { "THIS", THIS, 0 },
  131.   { "DAY*S", DAY, 0 },
  132.   { "WEEK*S", WEEK, 0 },
  133.   { "MONTH*S", WORD_MONTH, 0 },
  134.   { "YEAR*S", YEAR, 0 },
  135.   { "FORTNIGHT", FORTNIGHT, 0 },    /* two weeks (Australian) */
  136. /*  { "EVERY", EVERY, 0 }, */
  137.   { "FROM", FROM, 0 },
  138.   { "AFTER", AFTER, 0 },
  139.   { "BEFORE", BEFORE, 0 },
  140.   { "LAST", LAST, 0 },
  141.   { "THE", THE, 0 },
  142. /*  { "A", A, 0 }, */
  143.   { "AT", AT, 0 },
  144.   { "ON", ON, 0 },
  145.   { "IN", IN, 0 },
  146.   { "OF", OF, 0 },
  147. /*  { "AND", AND, 0 }, */
  148.   { "MORNING", TIMEKEY, 0 },    /* time keywords. Morning is 0:00 - 11:59 */
  149.   { "AFTERNOON", TIMEKEY, 12 }, /* Afternoon is 12:00 - 23:59 */
  150.   { "EVENING", TIMEKEY, 15 },    /* Evening is 15:00 - 02:59 */
  151.   { "NIGHT", TIMEKEY, 17 },    /* Night is 17:00 - 04:59 */
  152.   { "NOON", NOON, 12 },        /* time specifications */
  153.   { "MIDNIGHT", NOON, 24 },
  154.   { "ONE", NWORD, 1 },        /* numbers up to 19 */
  155.   { "TWO", NWORD, 2 },
  156.   { "THREE", NWORD, 3 },
  157.   { "FOUR", NWORD, 4 },
  158.   { "FIVE", NWORD, 5 },
  159.   { "SIX", NWORD, 6 },
  160.   { "SEVEN", NWORD, 7 },
  161.   { "EIGHT", NWORD, 8 },
  162.   { "NINE", NWORD, 9 },
  163.   { "TEN", NWORD, 10 },
  164.   { "ELEVEN", NWORD, 11 },
  165.   { "TWELVE", NWORD, 12 },
  166.   { "THIRTEEN", NWORD, 13 },
  167.   { "FOURTEEN", NWORD, 14 },
  168.   { "FIFTEEN", NWORD, 15 },
  169.   { "SIXTEEN", NWORD, 16 },
  170.   { "SEVENTEEN", NWORD, 17 },
  171.   { "EIGHTEEN", NWORD, 18 },
  172.   { "NINETEEN", NWORD, 19 },
  173.   { "FIRST", NTHWORD, 1 },        /* number up to 19th */
  174.   { "SECOND", NTHWORD, 2 },
  175.   { "THIRD", NTHWORD, 3 },
  176.   { "FOURTH", NTHWORD, 4 },
  177.   { "FIFTH", NTHWORD, 5 },
  178.   { "SIXTH", NTHWORD, 6 },
  179.   { "SEVENTH", NTHWORD, 7 },
  180.   { "EIGHT", NTHWORD, 8 },
  181.   { "NINTH", NTHWORD, 9 },
  182.   { "TENTH", NTHWORD, 10 },
  183.   { "ELEVENTH", NTHWORD, 11 },
  184.   { "TWELFTH", NTHWORD, 12 },
  185.   { "THIRTEENTH", NTHWORD, 13 },
  186.   { "FOURTEENTH", NTHWORD, 14 },
  187.   { "FIFTEENTH", NTHWORD, 15 },
  188.   { "SIXTEENTH", NTHWORD, 16 },
  189.   { "SEVENTEENTH", NTHWORD, 17 },
  190.   { "EIGHTEENTH", NTHWORD, 18 },
  191.   { "NINETEENTH", NTHWORD, 19 },
  192.   { "ST", ST, 0 },        /* for 1st */
  193.   { "ND", ND, 0 },        /* 2nd */
  194.   { "RD", RD, 0 },        /* 3rd */
  195.   { "TH", TH, 0 },        /* nth */
  196.   { "AM", AMPM, 0 },        /* time qualifiers */
  197.   { "A.M.", AMPM, 0 },
  198.   { "PM", AMPM, 12 },
  199.   { "P.M.", AMPM, 12 },
  200.   { "CHRISTMAS", CHRISTMAS, 1225 },    /* special dates */
  201.   { "NEW", NEW, 101 },
  202.  
  203.     /* time zones */
  204.   { "UT", ZONE, 0 },
  205.   { "GMT", ZONE, 0 },
  206.   { "UTC", ZONE, 0 },
  207.   { "GST", ZONE, -180},
  208.   { "GDT", ZONE, -120},
  209.   { "NST", ZONE, -210},
  210.   { "AST", ZONE, -240},
  211.   { "ADT", ZONE, -180},
  212.   { "EST", ZONE, -300 },
  213.   { "EDT", ZONE, -240 },
  214.   { "CST", ZONE, -360 },
  215.   { "CDT", ZONE, -300 },
  216.   { "MST", ZONE, -420 },
  217.   { "MDT", ZONE, -360 },
  218.   { "PST", ZONE, -480 },
  219.   { "PDT", ZONE, -420 },
  220.   { "HST", ZONE, -600 },
  221.   { "HDT", ZONE, -540 },
  222.   { "AHST", ZONE, -600 },
  223.   { "AHDT", ZONE, -540 },
  224.   { "AEST", ZONE, 600 },
  225.   { "AESST", ZONE, 660 },
  226.   { "ACST", ZONE, 570 },
  227.   { "ACSST", ZONE, 630 },
  228.   { "AWST", ZONE, 480 },
  229.   { "AWSST", ZONE, 540 },
  230.   { "EET", ZONE, 120 },
  231.   { "MET", ZONE, 60 },
  232.   { "WET", ZONE, 0 },
  233.   { "BST", ZONE, 60 },
  234.   { "MEZ", ZONE, 60 },
  235.   { "MESZ", ZONE, 120 },
  236.   { "CET", ZONE, 60 },
  237.   { "CEST", ZONE, 120 },
  238.     /* military time */
  239.   { "Z", ZONE, 0 },
  240.   { "A", ZONE, -60 },
  241.   { "B", ZONE, -120 },
  242.   { "C", ZONE, -180 },
  243.   { "D", ZONE, -240 },
  244.   { "E", ZONE, -300 },
  245.   { "F", ZONE, -360 },
  246.   { "G", ZONE, -420 },
  247.   { "H", ZONE, -480 },
  248.   { "I", ZONE, -540 },
  249. /* No 'J' -- it's not an accident */
  250.   { "K", ZONE, -600 },
  251.   { "L", ZONE, -660 },
  252.   { "M", ZONE, -720 },
  253.   { "N", ZONE, 60 },
  254.   { "O", ZONE, 120 },
  255.   { "P", ZONE, 180 },
  256.   { "Q", ZONE, 240 },
  257.   { "R", ZONE, 300 },
  258.   { "S", ZONE, 360 },
  259.   { "T", ZONE, 420 },
  260.   { "U", ZONE, 480 },
  261.   { "V", ZONE, 540 },
  262.   { "W", ZONE, 600 },
  263.   { "X", ZONE, 660 },
  264.   { "Y", ZONE, 720 },
  265.  
  266.   { 0, 0, 0 }
  267. };
  268.  
  269. %}
  270. %%
  271.  
  272. /* Grammar for legal dates. The date is returned in the dtm structure indexed
  273.  * by result. The time is returned in ptm (and, possibly, shour).
  274.  */
  275.  
  276. date_time :    sdate_time
  277.             { result = $$;
  278.               check ($$); }
  279.     ;
  280.  
  281. sdate_time :    stime
  282.             { $$ = new_dtm (CURRDATE); }
  283.     |    full_date time
  284.     |    full_date time year
  285.         /* 
  286.          * This is a hack. To handle the strange format
  287.          * day month day-in-month time year
  288.          * It parses the date as though it were a full
  289.          * specification and then substitutes the year.
  290.          */
  291.             { setrep (&(dtm[$$]), RTM);
  292.               dtm[$$].tm.tm_year = ptm.tm_year;
  293.               dtm[$$].tm.tm_wday = -1;
  294.               dtm[$$].tm.tm_yday = -1; } 
  295.     |    time ON full_date
  296.             { $$ = $3; }
  297.     |    time full_date
  298.             { $$ = $2; }
  299.     |    full_date
  300.     |    time
  301.             { $$ = new_dtm (CURRDATE); }
  302.     |    stime today
  303.             { $$ = new_dtm (CURRDATE);
  304.               incr ($$, $2); }
  305.     |    NOW
  306.             { $$ = new_dtm (CURRDATE);
  307.               ptm.tm_hour = currtm->tm_hour;
  308.               ptm.tm_min = currtm->tm_min;
  309.               ptm.tm_sec = currtm->tm_sec; }
  310.     ;
  311.  
  312. full_date :    rec_date
  313.     |    timekey _of rec_date
  314.             { $$ = $3; }
  315.     |    THE timekey _of rec_date
  316.             { $$ = $4; }
  317.     |    rec_date timekey
  318.     ;
  319.  
  320. rec_date :    date
  321.             { check ($$); }
  322.     ;
  323.  
  324. date    :    partial_date
  325.             { $$ = new_dtm (CURRDATE);
  326.               constrain (PTM, $$, ppf, 1); }
  327.     |    THIS relative_partial_date
  328.             { $$ = new_dtm (CURRDATE);
  329.               constrain (PTM, $$, FUTURE, 1); }
  330.     |    NEXT relative_partial_date
  331.             { $$ = new_dtm (CURRDATE);
  332.               constrain (PTM, $$, FUTURE, 2); }
  333.     |    LAST relative_partial_date
  334.             { $$ = new_dtm (CURRDATE);
  335.               incr ($$, -1);
  336.               constrain (PTM, $$, PAST, 1); }
  337.     |    weekday WEEK
  338.             { $$ = new_dtm (CURRDATE);
  339.               constrain (PTM, $$, FUTURE, 1);
  340.               incr ($$, 7); }
  341.     |    weekday FORTNIGHT
  342.             { $$ = new_dtm (CURRDATE);
  343.               constrain (PTM, $$, FUTURE, 1);
  344.               incr ($$, 14); }
  345.     |    adjusted_date
  346.     |    today
  347.             { $$ = new_dtm (CURRDATE);
  348.               incr ($$, $1); }
  349.     |    TODAY WEEK
  350.             { $$ = new_dtm (CURRDATE);
  351.               incr ($$, $1 + 7); }
  352.     |    TODAY FORTNIGHT
  353.             { $$ = new_dtm (CURRDATE);
  354.               incr ($$, $1 + 14); }
  355.     |    stddate
  356.             { $$ = new_dtm (PTM); }
  357.     |    weekday stddate
  358.             { $$ = new_dtm (PTM); }
  359.     ;
  360.  
  361. stddate    :    yearday year
  362.     |    yearday '-' year
  363.     |    yearday '/' year
  364.     ;
  365.  
  366. /* The following rule must precede other uses of relative_yearday and
  367.  * nonrelative_yearday so that the preferred reduction is to yearday.
  368.  * This, in turn, causes a 4-digit number to be preferred as a year
  369.  * rather than military time. */
  370.  
  371. yearday :    relative_yearday
  372.     |    nonrelative_yearday
  373.     ;
  374.  
  375. relative_partial_before : relative_partial_dtm
  376.     |    LAST relative_partial_date
  377.             { $$ = new_dtm (PTM); }
  378.     |    nth LAST weekday
  379.             { $$ = new_dtm (PTM);
  380.               dtm[$$].count = $1; }
  381.     ;
  382.  
  383. relative_partial_dtm :    relative_partial_date
  384.             { $$ = new_dtm (PTM); }
  385.     |    nth weekday
  386.             { $$ = new_dtm (PTM);
  387.               dtm[$$].count = $1; }
  388.     ;
  389.  
  390. /* 
  391.  * partial_date: Returns ptm.
  392.  */
  393.  
  394. partial_date : relative_partial_date
  395.     |    nonrelative_yearday
  396.     ;
  397.  
  398. relative_partial_date :    relative_yearday
  399.     |    weekday
  400.     |    nth
  401.             { ptm.tm_mday = $1; }
  402.     |    weekday nth
  403.             { ptm.tm_mday = $2; }
  404.     |    weekday relative_yearday
  405.     ;
  406.  
  407. weekday    :    WEEKDAY
  408.             { ptm.tm_wday = $1; }
  409.     ;
  410.  
  411. holiday :    sholiday
  412.             { ptm.tm_mon = $1/100-1; ptm.tm_mday = $1%100; }
  413.     ;
  414.  
  415. sholiday :    CHRISTMAS
  416.     |    CHRISTMAS DAY
  417.     |    NEW YEAR
  418.     ;
  419.  
  420. /* 
  421.  * forward_rec_date: Returns index to dtm.
  422.  */
  423.  
  424. forward_rec_date : AFTER rec_date
  425.             { $$ = $2; }
  426.     |    FROM rec_date
  427.             { $$ = $2; }
  428.     ;
  429.  
  430. /* 
  431.  * adjusted_date: Returns index to dtm.
  432.  */
  433.  
  434. adjusted_date :    days forward_rec_date
  435.             { $$ = $2;
  436.               incr ($$, $1); }
  437.     |    days BEFORE rec_date
  438.             { $$ = $3;
  439.               incr ($$, -$1); }
  440.     |    days AGO
  441.             { $$ = new_dtm (CURRTM);
  442.               incr ($$, -$1); }
  443.     |    months forward_rec_date
  444.             { $$ = $2;
  445.               incrmonth ($$, $1); }
  446.     |    months BEFORE rec_date
  447.             { $$ = $3;
  448.               incrmonth ($$, -$1); }
  449.     |    months AGO
  450.             { $$ = new_dtm (CURRDATE);
  451.               incrmonth ($$, -$1); }
  452.     |    years forward_rec_date
  453.             { $$ = $2;
  454.               incryear ($$, $1); }
  455.     |    years BEFORE rec_date
  456.             { $$ = $3;
  457.               incryear ($$, -$1); }
  458.     |    years AGO
  459.             { $$ = new_dtm (CURRDATE);
  460.               incryear ($$, -$1); }
  461.     |    rel_date
  462.     |    THE rel_date
  463.             { $$ = $2; }
  464.     ;
  465.  
  466. /* 
  467.  * rel_date: Parse date relative to another date. Returns index to
  468.  * dtm structure containing the resolved date.
  469.  */
  470.  
  471. rel_date :    relative_partial_dtm AFTER rec_date
  472.             { $$ = $3;
  473.               incr ($$, 1);
  474.               constrain ($1, $$, FUTURE, dtm[$1].count); }
  475.     |    relative_partial_before BEFORE rec_date
  476.             { $$ = $3;
  477.               incr ($$, -1);
  478.               constrain ($1, $$, PAST, dtm[$1].count); }
  479.     ;
  480.  
  481. /* 
  482.  * today: Parse an indication of the current day.
  483.  * Side effect: sets tmkey if time of day indication is also made.
  484.  */
  485.  
  486. today    :    TODAY
  487.     |    TONIGHT
  488.             { tmkey = 17; }
  489.     |    THIS timekey
  490.     |    TODAY timekey
  491.             { if ($1 == 0) yyerror (unkstring); }
  492.     ;
  493.  
  494. /* 
  495.  * months: Parse a number of months. Returns integer the number of months.
  496.  */
  497.  
  498. months    :    number WORD_MONTH
  499.             { $$ = $1; }
  500.     |    A WORD_MONTH
  501.             { $$ = 1; }
  502.     ;
  503.  
  504. /* 
  505.  * years: Parse a number of years. Returns integer the number of years.
  506.  */
  507.  
  508. years    :    number YEAR
  509.             { $$ = $1; }
  510.     |    A YEAR
  511.             { $$ = 1; }
  512.     ;
  513.  
  514. /* 
  515.  * days: Parse a number of days. Returns integer number of days.
  516.  */
  517.  
  518. days    :    number DAY
  519.     |    nth_day
  520.     |    the_nth_day
  521.     |    THE DAY
  522.             { $$ = 1; }
  523.     |    A DAY
  524.             { $$ = 1; }
  525.     |    DAY
  526.             { $$ = 1; }
  527.     |    number WEEK
  528.             { $$ = $1 * 7; }
  529.     |    A WEEK
  530.             { $$ = 7; }
  531.     |    WEEK
  532.             { $$ = 7; }
  533.     |    A FORTNIGHT
  534.             { $$ = 14; }
  535.     |    FORTNIGHT
  536.             { $$ = 14; }
  537.     ;
  538.  
  539. /* 
  540.  * nonrelative_yearday: Parse day of the year.
  541.  * Returns parsed specification in ptm.
  542.  */
  543.  
  544. nonrelative_yearday : month_name THE nth
  545.             { ptm.tm_mday = $3; }
  546.     |    stdmonthday _of month_name
  547.     |    THE stdmonthday _of month_name
  548.     |    the_nth_day OF month_name
  549.             { ptm.tm_mday = $1; }
  550.     |    nth_day OF month_name
  551.             { ptm.tm_mday = $1; }
  552.     ;
  553.  
  554. /* 
  555.  * relative_yearday: Parse day within year. Return ptm.
  556.  */
  557.  
  558. relative_yearday : month '/' monthday
  559.     |    month_name monthday
  560.     |    month_name '-' monthday
  561.     |    stdmonthday '-' month_name
  562.     |    stdmonthday '-' month
  563.     |    holiday
  564.     ;
  565.  
  566. /* 
  567.  * time: Returns ptm containing the parsed time spec.
  568.  */
  569.  
  570. time    :    ttime
  571.     |    AT ttime
  572.     |    AT stime
  573.     ;
  574.  
  575. stime    :    simple_time
  576.     |    simple_time IN THE timekey
  577.     ;
  578.  
  579. simple_time :    number
  580.             { if ($1 < 1 || $1 > 12) yyerror (unkstring);
  581.               shour = $1; }
  582.     ;
  583.  
  584. timekey :    TIMEKEY
  585.             { tmkey = $1; }
  586.     ;
  587.  
  588. /* 12am is midnight. i.e. 0:00.  12pm is noon. */
  589. ttime    :    hm_time
  590.     |    hm_time IN THE timekey
  591.     |    hour ':' min ':' sec opt_hundredths zone
  592.     |    hour ':' min ':' sec opt_hundredths
  593.     |    whour AMPM
  594.             { tmkey = $2; }
  595.     |    hm_time AMPM
  596.             { tmkey = $2; }
  597.     |    NOON
  598.             { ptm.tm_hour = $1; }
  599.     |    whour NOON
  600.             { if ($1 != 12) yyerror (unkstring);
  601.               shour = -1;
  602.               ptm.tm_hour = $2; }
  603.     |    hm_time NOON
  604.             { if (shour != 12 || ptm.tm_min != 0) yyerror (unkstring);
  605.               shour = -1;
  606.               ptm.tm_hour = $2; }
  607.     |    NUMBER4 
  608.             { ptm.tm_hour = $1 / 100;
  609.               ptm.tm_min = $1 % 100;
  610.               if (ptm.tm_min > 59) yyerror (unkstring);
  611.               if ($1 > 2400) yyerror (unkstring); }
  612.     ;
  613.  
  614. opt_hundredths :    '.' NUMBER
  615.     |
  616.     ;
  617.  
  618. hm_time    :    hour ':' min zone
  619.     |    hour ':' min
  620.     |    hour '.' min zone
  621.     |    hour '.' min
  622.     ;
  623.  
  624. zone    :    ZONE
  625.             { zone = $1; }
  626.     |    '-' ZONE
  627.             { zone = $2; }
  628.     |    '+' NUMBER4
  629.             { zone = ($2/100 * 60) + ($2 % 100); }
  630.     |    '-' NUMBER4
  631.             { zone = -(($2/100 * 60) + ($2 % 100)); }
  632.     ;              
  633.  
  634. month_name :    MONTH
  635.             { ptm.tm_mon = $1; }
  636.     ;
  637.  
  638. /* 
  639.  * year: Parse year specification.
  640.  * Returns ptm.tm_year set to the year specified.
  641.  */
  642.  
  643. year    :    NUMBER
  644.             { ptm.tm_year = $1 + (($1>=100)?-1900:0); }
  645.     |    NUMBER4
  646.             { ptm.tm_year = $1 - 1900; }
  647.     |    THIS YEAR
  648.             { ptm.tm_year = currtm->tm_year; }
  649.     |    LAST YEAR
  650.             { ptm.tm_year = currtm->tm_year - 1; }
  651.     |    NEXT YEAR
  652.             { ptm.tm_year = currtm->tm_year + 1; }
  653.     ;
  654.  
  655. /* 
  656.  * month: Parse numeric month. Store (0 origin) in ptm.tm_mon.
  657.  */
  658.  
  659. month    :    NUMBER
  660.             { ptm.tm_mon = $1 - 1; }
  661.     ;
  662.  
  663. monthday :    NWORD
  664.             { ptm.tm_mday = $1; }
  665.     |    stdmonthday
  666.     ;
  667.  
  668. stdmonthday :    NUMBER
  669.             { ptm.tm_mday = $1; }
  670.     |    nth
  671.             { ptm.tm_mday = $1; }
  672.     ;
  673.  
  674. hour    :    HOUR
  675.             { ptm.tm_hour = $1; }
  676.     |    SHOUR
  677.             { shour = $1; }
  678.     ;
  679.  
  680. whour    :    hour
  681.     |    NWORD
  682.             { shour = $1; }
  683.     ;
  684.  
  685. min    :    NUMBER
  686.             { ptm.tm_min = $1; }
  687.     |    HOUR
  688.             { ptm.tm_min = $1; }
  689.     |    SHOUR
  690.             { ptm.tm_min = $1; }
  691.     ;
  692.  
  693. sec    :    NUMBER
  694.             { ptm.tm_sec = $1; }
  695.     ;
  696.  
  697. number    :    NWORD
  698.     |    NUMBER
  699.     ;
  700.  
  701. /* 
  702.  * nth: Parse 1st 2nd etc or first second etc.
  703.  * Return integer value of the number.
  704.  */
  705.  
  706. nth    :    NTHWORD
  707.     |    NUMBER ST
  708.             { if ($1 % 10 != 1 || $1 % 100 == 11) yyerror (unkstring);
  709.               $$ = $1; }
  710.     |    NUMBER ND
  711.             { if ($1 % 10 != 2 || $1 % 100 == 12) yyerror (unkstring);
  712.               $$ = $1; }
  713.     |    NUMBER RD
  714.             { if ($1 % 10 != 3 || $1 % 100 == 13) yyerror (unkstring);
  715.               $$ = $1; }
  716.     |    NUMBER TH
  717.             { if (($1 + 9) % 10 <= 2 && ($1 % 100) / 10 != 1)
  718.                 yyerror (unkstring);
  719.               $$ = $1; }
  720.     ;
  721.  
  722. _of    :    OF
  723.     |
  724.     ;
  725.  
  726. the_nth_day :    THE nth_day
  727.             { $$ = $2; }
  728.     ;
  729.  
  730. nth_day    :    nth DAY
  731.     ;
  732. %%
  733.  
  734. /*
  735.  *  pdate - parse date specification into time structure
  736.  *
  737.  *  str = date specification string
  738.  *  tmp = pointer to time structure to receive parsed fields
  739.  *
  740.  *      Parses the supplied date string and sets the appropriate
  741.  *  fields into the time structure.  Fields which are not specified
  742.  *  in the string are returned as -1 or filled according to select (below).
  743.  *  The date string is copied into a temporary buffer before
  744.  *  being parsed (maximum of 80 characters).
  745.  *
  746.  *     Select may be used to prefer dates in the past or future.
  747.  *  true indicates past, false indicates future. Unspecified
  748.  *  fields are filled (if possible) to give a reasonable date in the past
  749.  *  or future (or the current date).
  750.  *
  751.  *  e.g.    Thursday would mean "last Thursday" if select was true,
  752.  *        "this Thursday" if select was false.
  753.  *
  754.  *  Note that "today" is the date passed in the date structure.
  755.  *
  756.  *     Returns 0 on a successful parse, -1 on error.
  757.  */
  758.  
  759. /* pdate: a cover function for parsedate which is not completely
  760.  *   compatible with the original pdate().
  761.  */
  762.  
  763. /* pdate (str, tmp)
  764. char *str;
  765. struct tm *tmp;
  766. { return (parsedate (str, tmp, 1, 0, 0));
  767. }
  768. */
  769.  
  770. #define MAXSTRLEN 150
  771.  
  772. parsedate (str, tmp, settm, select, err, gmt)
  773.     char *str;
  774.     struct tm *tmp;
  775.     int settm, select, err;
  776.     long *gmt;
  777. {
  778.     long time (), curtim;
  779.     char tstr[MAXSTRLEN+1];
  780.     int i;
  781.     register struct tm *rtm;
  782.  
  783.     junk_err = err;
  784.     if (settm)
  785.     { curtim = time (0);
  786.     /* Current RT compiler didn't like this...   juo */
  787. /*      *currtm = *localtime (&curtim); */    
  788.         bcopy (localtime (&curtim), currtm, sizeof (struct tm) );
  789.     }
  790.     else
  791.     { *currtm = *tmp;
  792.     }
  793.  
  794.     /*  initialize lexical analyzer  */
  795.     for (i = 0; i < NDTMS; i++)
  796.       dtmused[i] = 0;
  797.     strncpy(strp = tstr, str, MAXSTRLEN);
  798.     tstr[MAXSTRLEN] = 0;
  799.     ptm.tm_year = NOYEAR;
  800.     ptm.tm_mon = -1;
  801.     ptm.tm_mday = -1;
  802.     ptm.tm_wday = -1;
  803.     ptm.tm_yday = -1;
  804.     ptm.tm_hour = -1;
  805.     ptm.tm_min = -1;
  806.     ptm.tm_sec = -1;
  807.     pcount = 0;
  808.     currtm->tm_yday = currtm->tm_wday = -1;
  809.     shour = -1;
  810.     tmkey = -1;
  811. /*    zone = 0; */
  812.     zone = -1;
  813.     delim = 0;
  814.     ppf = select;
  815.  
  816.     if (setjmp(errbuf) == 0)
  817.     {
  818.     yyparse();
  819.     rtm = &(dtm[result].tm);
  820.     setrep (&dtm[result], RTM);
  821.     /* fill in midnight if no time was specified -- ALD/ISI */
  822.     rtm->tm_hour = (ptm.tm_hour != -1)? ptm.tm_hour: 0;
  823.     rtm->tm_min = (ptm.tm_min != -1)? ptm.tm_min: 0;
  824.     rtm->tm_sec = (ptm.tm_sec != -1)? ptm.tm_sec: 0;
  825.     time_def (rtm, currtm);
  826.     /* If time is 24:00 or later, advance date to next day. */
  827.     if (rtm->tm_hour >= 24)
  828.     { incr (result, rtm->tm_hour / 24);
  829.       setrep (&dtm[result], RTM);
  830.       rtm->tm_hour = rtm->tm_hour % 24;
  831.     }
  832.     if (dtm[result].repn == RTM && dtm[result].tm.tm_yday == -1)
  833.       setrep (&dtm[result], RDAYS);
  834.  
  835.     /* fixed timezone processing!? -- ALD/ISI */
  836.     setrep (&dtm[result], RTM);
  837.     
  838.     /* Fix up for time zone */
  839.     timezone(gmt);
  840.     bcopy(gmtime(gmt),tmp,sizeof(struct tm));
  841.  
  842.     return(0);            /* return here on successful parse */
  843.     }
  844.     else
  845.     return(CERROR);            /* return here on error */
  846.  
  847. }
  848.  
  849. /*
  850.  *  yyerror - error routine (called by yyparse)
  851.  *
  852.  *     Performs a jump to the error return location established
  853.  *  by pdate().
  854.  */
  855.  
  856. static yyerror(errstring)
  857. char *errstring;
  858. {
  859.  
  860.     longjmp(errbuf, 1);
  861.  
  862. }
  863.  
  864. static int prelval;
  865. static struct { int token; int val; } peep[MAXPEEP];
  866. static int peepb = 0, peepe = 0;
  867. static int lead0;
  868.  
  869. /*  yylex - return next token in date string.
  870.  * 
  871.  *  Obtains the tokens from nextlex() and returns them. Checks for
  872.  *  NUMBER tokens which are really HOURs. This is done by peeping ahead.
  873.  *  It gives more lookahead than yacc can support.
  874.  */
  875.  
  876. static int yylex ()
  877. { register int ctoken, htoken;
  878.   ctoken = nextlex();
  879.   htoken = lead0 ? HOUR : SHOUR;
  880.   if (ctoken == NUMBER && yylval <= 24)
  881.   { /* Possible hour - check it out */
  882.     peeper (1);                  /* Allow 1 token peeping */
  883.     if (peep[0].token == AMPM || peep[0].token == NOON)
  884.       return (htoken);                  /* NN AM or NN PM or 12 NOON */
  885.     if (peep[0].token == ':' || peep[0].token == '.')
  886.     { peeper (2);
  887.       if (peep[1].token == NUMBER)
  888.     return (htoken);             /* NN:NN or NN.NN */
  889.     }
  890.   }
  891.   return (ctoken);
  892. }
  893.  
  894. static int nextlex ()
  895. { register int token;
  896.   if (peepb < peepe)
  897.   { token = peep[peepb].token;
  898.     yylval = peep[peepb].val;
  899.     peepb++;
  900.   }
  901.   else
  902.   { token = prelex ();
  903.     yylval = prelval;
  904.   }
  905.   return (token);
  906. }
  907.  
  908. static peeper (n)
  909. int n;
  910. { register int i;
  911.   if (peepb != 0)
  912.   { for (i = peepb; i < peepe; i++)
  913.       peep[i-peepb] = peep[i];
  914.     peepe -= peepb;
  915.   }
  916.   peepb = 0;
  917.   while (peepe < n)
  918.   { peep[peepe].token = prelex ();
  919.     peep[peepe].val = prelval;
  920.     peepe++;
  921.   }
  922. }
  923.  
  924. /*
  925.  *  prelex - return next token in date string
  926.  *
  927.  *     Uses nxtarg() to parse the next field of the date string.
  928.  *  If a non-space, tab, comma or newline delimiter terminated the
  929.  *  previous field it is returned before the next field is parsed.
  930.  *
  931.  *     Returns either one of the delimiter characters " -:/.", the token
  932.  *  from a match on the table (with the associated value in yylval), or
  933.  *  NUMBER, or JUNK.
  934.  *  JUNK is any unrecognized token (depending on the call arguments to
  935.  *  parsedate - an unrecognized token may instead be returned as -1 simply
  936.  *  terminating the parse.)
  937.  *  NUMBER is a numeric string.  NUMBER4 is a 4-digit number.
  938.  *  If the numeric string commences with 0, then lead0 is true.
  939.  *  '@' sign is treated as the token AT.
  940.  */
  941.  
  942. static int wasabb;            /* tabfind indicates abbrev. */
  943.  
  944. static int prelex()
  945. {
  946.  
  947.   register int ret;            /* temp for previous delimiter */
  948.   register char *fp;            /* current field */
  949.   register int find, ndig;
  950.   extern char _argbreak;        /* current delimiter */
  951.  
  952.   while (1)
  953.   { if (ret=delim)
  954.     {
  955.     delim = 0;
  956.         if (ret == '@')
  957.         return (AT);
  958.         /* 
  959.          * Ignore all but the good characters
  960.          */
  961.     if (ret != ':' && ret != '/' && ret != '-' && ret != '.' && ret != '+')
  962.         ret = 0;
  963.         if (ret != 0)
  964.       return (ret);
  965.     }
  966.     if (*strp == 0) return (0);
  967.     while (*strp == ' ' || *strp == '\t' || *strp == '\n')
  968.       strp++;
  969.  
  970.     if (*strp >= '0' && *strp <= '9')
  971.     { prelval = 0;
  972.       ndig = 0;
  973.       lead0 = *strp == '0';
  974.       while (*strp >= '0' && *strp <= '9')
  975.       { prelval = prelval * 10 + *strp - '0';
  976.     strp++;
  977.     ndig++;
  978.       }
  979.       if (ndig == 4)
  980.         return (NUMBER4);
  981.       return (NUMBER);
  982.     }
  983.     fp = nxtarg (&strp, " \t,-+:/.@()[]\n");
  984.     delim = _argbreak;
  985.     if (*fp == 0 && delim == 0) return (0);  /* End of input string */
  986.     if (*fp != 0)                  /* skip null tokens */
  987.     { (void) foldup(fp, fp);
  988.       /* Because of the embedded period, a.m. and p.m. do not work.
  989.        * Solution is to recognize them explicitly.
  990.        */
  991.       if (fp[1] == '\0' && (delim == '.' || delim == ' ') &&
  992.         (*fp == 'A' || *fp == 'P') && (*strp == 'M' || *strp == 'm') && 
  993.         (strp[1] == '.' || strp[1] == ' ' || strp[1] == '\t' ||
  994.           strp[1] == '\0'))
  995.       { /* It is a.m. or p.m. */
  996.         prelval = *fp == 'A' ? 0 : 12;
  997.         strp += 2;    /* Skip the m. */
  998.         delim = 0;
  999.         return (AMPM);
  1000.       }
  1001.       if ((find = tabfind(fp, strings)) >= 0)
  1002.       {
  1003.     if (wasabb && delim == '.')    /* If tabfind found abbrev */
  1004.       delim = 0;            /* Discard period after abbrev. */
  1005.         prelval = strings[find].lexval;
  1006.         return(strings[find].token);
  1007.       }
  1008.       if (junk_err)
  1009.         return (JUNK);
  1010.       else
  1011.     return (0);
  1012.     }
  1013.   }
  1014. }
  1015.  
  1016. /* subroutines useful for manipulating dates and used by
  1017.  * parsedate.y.
  1018.  * 
  1019.  * Copyright (c) Leonard G C Hamey, December 1983.
  1020.  * 
  1021.  * date_days (tm): converts the date portion of tm structure to days since
  1022.  *   1 jan 1900 + 693960. Also can be used to obtain weekday (sunday == 0)
  1023.  *   by taking modulo 7.
  1024.  *
  1025.  * days_date: converts days since 1/1/1900 + 693960 to date in tm structre.
  1026.  * 
  1027.  * tabfind: searches a table of keywords for date parsing.
  1028.  * 
  1029.  * constrain: fills in default fields under control of past/future
  1030.  *     parameter.
  1031.  */
  1032.  
  1033. static int date_days (tm)
  1034. struct tm *tm;
  1035. {   /* Number of days since 1/1/1900 + 693960 */
  1036.     int dd = tm->tm_mday, mm = tm->tm_mon + 1, fullyear = tm->tm_year + 1900;
  1037.     int f;
  1038.  
  1039.     if (mm >= 3)
  1040.     {   f=365*fullyear+dd+31*mm-31-(4*mm+23)/10+fullyear/4-(75+(fullyear/100)*75)/100;
  1041.     }
  1042.     else
  1043.     {   f=365*fullyear+dd+31*mm-31+(fullyear-1)/4-(75+((fullyear-1)/100)*75)/100;
  1044.     }
  1045.  
  1046.     return (f-1);
  1047. }
  1048.  
  1049. static int monthend[] =
  1050. { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
  1051.  
  1052. static days_date (d, tm)
  1053. int d;
  1054. struct tm *tm;
  1055. {   /* Number of days since 1/1/1900 -> mm/dd/fullyear and weekday */
  1056.     int t = d - 693960, leap;
  1057.     int mm, fullyear;
  1058.     tm->tm_year = 0;                  /* 1900 */
  1059.     tm->tm_mon = 0;                  /* Jan */
  1060.     tm->tm_mday = 1;
  1061.     while (t < 0)                 /* handle dates before 1900 */
  1062.     {   tm->tm_year += t / 366 - 1;
  1063.         t = d - date_days (tm);
  1064.     }
  1065.     while (t >= 366)
  1066.     {   tm->tm_year += t / 366;
  1067.         t = d - date_days (tm);
  1068.     }
  1069.     fullyear = tm->tm_year + 1900;
  1070.     leap = yearsize (fullyear) == 366;
  1071.     if (! leap && t == 365)
  1072.     { tm->tm_year++;
  1073.       t = 0;
  1074.     }
  1075.     tm->tm_yday = t;                 /* Day in year (0-365) */
  1076.     if (! leap && t >= 59)              /* If after Feb non leap */
  1077.         t++;                      /* Fudge as if leap */
  1078.     mm = t / 31;                  /* Guess the month */
  1079.     if (t >= monthend[mm+1])              /* Check the guess */
  1080.         mm++;
  1081.     tm->tm_mday = t - monthend[mm] + 1;         /* Compute day in month */
  1082.     tm->tm_mon = mm;
  1083.     tm->tm_wday = d % 7;              /* Sunday = 0 */
  1084. }
  1085.  
  1086. static int tabfind (text, table)
  1087. char *text;
  1088. struct stable *table;
  1089. { int tp;
  1090.   char *tep, *txp;
  1091.   int find = -1, foundstar;
  1092.   wasabb = 0;
  1093.   for (tp = 0; table[tp].text; tp++)
  1094.   { tep = table[tp].text;
  1095.     txp = text;
  1096.     foundstar = 0;
  1097.     while (1)
  1098.     { if (*tep == '*')
  1099.       { foundstar = 1;
  1100.     tep++;
  1101.       }
  1102.       if (! *txp)                  /* If end of text */
  1103.       { if (! *tep)                  /* If also end of table entry */
  1104.       return (tp);                 /* then found */
  1105.     if (foundstar)
  1106.     { if (find >= 0)
  1107.         return (-2);              /* Ambiguous */
  1108.       find = tp;                  /* Remember partial match */
  1109.       wasabb = 1;                 /* Was abbrev. */
  1110.       break;
  1111.     }
  1112.       }
  1113.       if (*txp != *tep)
  1114.     break;                      /* No match */
  1115.       tep++;  txp++;
  1116.     }
  1117.   }
  1118.   return (find);
  1119. }
  1120.  
  1121. /* check: check that a date is valid. each of the constraint processing
  1122.  *   routines is called in turn and if any of them do anything then the
  1123.  *   date is invalid.
  1124.  */
  1125.  
  1126. static check (date)
  1127. int date;
  1128. { register int did;
  1129.   register struct dtm *d = &dtm[date];
  1130.   if (d->repn != RTM)
  1131.     return;
  1132.   did = month (d, d->tm.tm_mon, FUTURE);
  1133.   if (! did)
  1134.     did = mday (d, d->tm.tm_mday, FUTURE);
  1135. #ifdef NOTDEF
  1136.   if (! did && d->tm.tm_wday >= 0)
  1137.     did = weekday (d, d->tm.tm_wday, FUTURE);
  1138.   if (did)
  1139.     yyerror (unkstring);
  1140. #endif NOTDEF
  1141.   return;
  1142. }
  1143.  
  1144. /* constrain: fill in defaults info in date. con is a dtm containing the 
  1145.  *   constraints (or -1 indicating to use ptm). date is the dtm containing
  1146.  *   the date to be constrained. repeat is the loop count. */
  1147.  
  1148. static constrain (con, date, past, repeat)
  1149. int con, date;
  1150. int past, repeat;
  1151. { register int n;
  1152.   register int did;
  1153.   register struct tm *c;
  1154.   register struct dtm *d = &dtm[date];
  1155.   if (con >= 0)
  1156.     c = &(dtm[con].tm);
  1157.   else
  1158.     c = &ptm;
  1159.   if (c->tm_year != NOYEAR)
  1160.     yyerror (unkstring);
  1161.  
  1162.   for (n = 1000, did = 0; ; did = 0)
  1163.   { if (c->tm_mon >= 0)
  1164.     { did |= month (d, c->tm_mon, past);
  1165.     }
  1166.     if (c->tm_mday >= 0)
  1167.     { did |= mday (d, c->tm_mday, past);
  1168.     }
  1169.     if (c->tm_wday >= 0)
  1170.     { did |= weekday (d, c->tm_wday, past);
  1171.     }
  1172.     if (! did)
  1173.     { if (repeat-- <= 1)
  1174.         break;
  1175.       incr (date, past ? -1 : 1);
  1176.     }
  1177.     if (--n <= 0)
  1178.       yyerror (unkstring);
  1179.   }
  1180.  
  1181.   if (con >= 0)
  1182.     dtmused[con] = 0;
  1183.   else
  1184.   { ptm.tm_year = NOYEAR;
  1185.     ptm.tm_mon = -1;
  1186.     ptm.tm_mday = -1;
  1187.     ptm.tm_wday = -1;
  1188.     ptm.tm_yday = -1;
  1189.   }
  1190. }
  1191.  
  1192. static time_def (tm, currtm)
  1193. struct tm *tm, *currtm;
  1194. { if (shour >= 0)            /* Handle simple hour specification */
  1195.   { if (tmkey == -1)            /* and combine it with time key. */
  1196.       tmkey = 8;            /* Default is 8:00 - 19:59 */
  1197.     if (shour == 12)
  1198.       shour = 0;
  1199.     if (shour < tmkey)
  1200.       shour += 12;
  1201.     if (shour < tmkey)
  1202.       shour += 12;
  1203.     tm->tm_hour = shour;
  1204.   }
  1205.   if (tm->tm_hour >= 0)
  1206.   { /* If time specified and fields left out, assume zero. */
  1207.     if (tm->tm_min < 0)
  1208.       tm->tm_min = 0;
  1209.     if (tm->tm_sec < 0)
  1210.       tm->tm_sec = 0;
  1211.   }
  1212. }
  1213.  
  1214. /* date constraint processing routines.
  1215.  * 
  1216.  * These routines allow determination of the first date after/before
  1217.  * (but possibly equal to the existing date) which satisfies the given
  1218.  * constraint(s).
  1219.  */
  1220.  
  1221. /* The constraints are implemented by calling the appropriate routine(s)
  1222.  * which check whether the particular constraint is satisfied, and if it
  1223.  * is not, advances the date until the constraint is satisfied.
  1224.  * 
  1225.  * The date is stored in the dtm structure.
  1226.  */
  1227.  
  1228. /* weekday: constrains the day of the week. */
  1229.  
  1230. static int weekday (dtm, wkday, past)
  1231. struct dtm *dtm;
  1232. int wkday;                      /* 0 = Sunday */
  1233. int past;                      /* true = past */
  1234. { int n;
  1235.   setrep (dtm, RDAYS);
  1236.   n = wkday - dtm->days % 7;          /* adjustment */
  1237.   if (past)
  1238.   { if (n > 0)
  1239.       n -= 7;
  1240.   }
  1241.   else
  1242.     if (n < 0)
  1243.       n += 7;
  1244.   dtm->days += n;
  1245.   return (n != 0);
  1246. }
  1247.  
  1248. /* month: constrains the month to the specified value. */
  1249.  
  1250. static int month (dtm, mon, past)
  1251. struct dtm *dtm;
  1252. int mon;
  1253. int past;
  1254. { setrep (dtm, RTM);
  1255.   if (mon < 0 || mon > 11)
  1256.     yyerror (unkstring);
  1257.   if (dtm->tm.tm_mon != mon)
  1258.   { if (past)
  1259.     { if (dtm->tm.tm_mon < mon)          /* If earlier month */
  1260.     dtm->tm.tm_year--;              /* Back up a year */
  1261.       dtm->tm.tm_mday = monthend[mon+1] - monthend[mon];
  1262.       if (mon == 1 && yearsize (dtm->tm.tm_year+1900) == 365)  /* Feb */
  1263.     dtm->tm.tm_mday--;
  1264.     }
  1265.     else
  1266.     { if (dtm->tm.tm_mon > mon)          /* If later month */
  1267.     dtm->tm.tm_year++;              /* Advance a year */
  1268.       dtm->tm.tm_mday = 1;
  1269.     }
  1270.     dtm->tm.tm_mon = mon;
  1271.     dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
  1272.     return (1);
  1273.   }
  1274.   return (0);
  1275. }
  1276.  
  1277. /* mday: constrains the month day to the specified value. Also
  1278.  *   checks the validity of the specified value and, if it is invalid,
  1279.  *   adjusts the month to compensate. */
  1280.  
  1281. static int mday (dtm, day, past)
  1282. struct dtm *dtm;
  1283. int day;
  1284. int past;
  1285. { register int maxday;
  1286.   register int status = 0;
  1287.   setrep (dtm, RTM);
  1288.   if (dtm->tm.tm_mday != day)
  1289.   { if (past)
  1290.     { if (dtm->tm.tm_mday < day)           /* Earlier day */
  1291.         if (dtm->tm.tm_mon-- == 0)         /* Back up a month */
  1292.     { dtm->tm.tm_mon = 11;
  1293.       dtm->tm.tm_year--;
  1294.     }
  1295.     }
  1296.     else
  1297.     { if (dtm->tm.tm_mday > day)           /* Later day */
  1298.     if (dtm->tm.tm_mon++ == 11)           /* Advance month */
  1299.     { dtm->tm.tm_mon = 0;
  1300.       dtm->tm.tm_year++;
  1301.     }
  1302.     }
  1303.     dtm->tm.tm_mday = day;
  1304.     dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
  1305.     status = 1;
  1306.   }
  1307.   if (day >= 28)
  1308.   { maxday = monthend[dtm->tm.tm_mon+1] - monthend[dtm->tm.tm_mon];
  1309.     if (dtm->tm.tm_mon == 1 && yearsize (dtm->tm.tm_year + 1900) == 365)
  1310.       maxday--;
  1311.     if (day > maxday)
  1312.     { if (past)
  1313.         dtm->tm.tm_mday = maxday;
  1314.       else
  1315.       { dtm->tm.tm_mday = 1;
  1316.     if (dtm->tm.tm_mon++ == 11)
  1317.     { dtm->tm.tm_mon = 0;
  1318.       dtm->tm.tm_year++;
  1319.     }
  1320.       }
  1321.       dtm->tm.tm_wday = dtm->tm.tm_yday = -1;
  1322.       return (1);
  1323.     }
  1324.   }
  1325.   return (status);
  1326. }
  1327.  
  1328. /* setrep: sets the representation of the date in a dtm structure. */
  1329.  
  1330. static setrep (dtm, rep)
  1331. struct dtm *dtm;
  1332. int rep;
  1333.   if (dtm->repn == rep)
  1334.     return;
  1335.  
  1336.   if (dtm->repn == RDAYS)
  1337.   { if (rep == RTM)
  1338.       days_date (dtm->days, &(dtm->tm));
  1339.   }
  1340.   else if (dtm->repn == RTM)
  1341.   { if (rep == RDAYS)
  1342.       dtm->days = date_days (&(dtm->tm));
  1343.   }
  1344.  
  1345.   dtm->repn = rep;
  1346.   return;
  1347. }
  1348.  
  1349. /* yearsize: returns nuber of days in year. */
  1350.  
  1351. yearsize (year)
  1352. int year;
  1353. { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) ? 366 : 365);
  1354. }
  1355.  
  1356. /* new_dtm: returns the index of a new dtm structure. If current is CURRTM,
  1357.  *   the structure will contain a copy of the current date-time, else it
  1358.  *   will contain a copy of ptm, and ptm will be reset to all -1.
  1359.  */
  1360.  
  1361. static int new_dtm (current)
  1362. int current;
  1363. { register int i;
  1364.   for (i = 0; i < NDTMS; i++)
  1365.     if (! dtmused[i])
  1366.     { dtmused[i] = 1;
  1367.       if (current == CURRTM || current == CURRDATE)
  1368.       { dtm[i].tm = *currtm;
  1369.         if (current == CURRDATE)
  1370.     { dtm[i].tm.tm_hour = -1;
  1371.       dtm[i].tm.tm_min = -1;
  1372.       dtm[i].tm.tm_sec = -1;
  1373.     }
  1374.     dtm[i].repn = RTM;
  1375.     dtm[i].count = 0;
  1376.       }
  1377.       else
  1378.       { dtm[i].tm = ptm;
  1379.     dtm[i].count = pcount;
  1380.     dtm[i].repn = RTM;
  1381.     ptm.tm_year = NOYEAR;
  1382.     ptm.tm_mon = -1;
  1383.     ptm.tm_mday = -1;
  1384.     ptm.tm_wday = -1;
  1385.     ptm.tm_yday = -1;
  1386.     pcount = 0;
  1387.       }
  1388.       return (i);
  1389.     }
  1390.   yyerror (unkstring);
  1391.   return(-1); /* Never reached */
  1392. }
  1393.  
  1394. /* incr: increment date in dtm structure. */
  1395.  
  1396. static incr (ndtm, days)
  1397. int ndtm;
  1398. int days;
  1399. { setrep (&dtm[ndtm], RDAYS);
  1400.   dtm[ndtm].days += days;
  1401. }
  1402.  
  1403. static incryear (ndtm, years)
  1404. int ndtm;
  1405. int years;
  1406. { setrep (&dtm[ndtm], RTM);
  1407.   dtm[ndtm].tm.tm_year += years;
  1408.   dtm[ndtm].tm.tm_wday = -1;        /* Unknown */
  1409.   dtm[ndtm].tm.tm_yday = -1;        /* Unknown */
  1410. }
  1411.  
  1412. /* incrmonth: increment date in dtm structure by a number of months.*/
  1413.  
  1414. static incrmonth (ndtm, months)
  1415. int ndtm;
  1416. int months;
  1417. { int inc;
  1418.   inc = months > 0 ? 1 : -1;
  1419.   setrep (&dtm[ndtm], RTM);        /* Use tm structure repn */
  1420.   for ( ; months != 0; months -= inc)
  1421.   { dtm[ndtm].tm.tm_mon += inc;
  1422.     if (dtm[ndtm].tm.tm_mon < 0)
  1423.     { dtm[ndtm].tm.tm_mon = 11;
  1424.       dtm[ndtm].tm.tm_year--;
  1425.     }
  1426.     else if (dtm[ndtm].tm.tm_mon > 11)
  1427.     { dtm[ndtm].tm.tm_mon = 0;
  1428.       dtm[ndtm].tm.tm_year++;
  1429.     }
  1430.   }
  1431.   dtm[ndtm].tm.tm_wday = -1;         /* Day of week is unknown */
  1432.   dtm[ndtm].tm.tm_yday = -1;        /* Day of year is unknown */
  1433. }
  1434.  
  1435. static long offset()
  1436. {
  1437.     struct timeval tp;
  1438.     struct timezone tzp;
  1439.     register struct tm *now;
  1440.     int gmtYear, gmtDay, gmtHour, gmtMin;
  1441.     static int virgin = 1;
  1442.     static long GMTDiff;    /* what to add to GMT (in minutes) to get
  1443.             local time (in minutes), or ``minutesEast'' */
  1444.  
  1445.     if (!virgin) return GMTDiff;
  1446.     virgin = 0;
  1447.  
  1448.     /*
  1449.        The zone returned by gettimeofday is static for the life of the kernel;
  1450.        it is localtime() that judges whether the local time is daylight or not.
  1451.        Get the GMT info and compare it to the localtime result to determine
  1452.        the current offset from GMT.
  1453.     */
  1454.     gettimeofday(&tp, &tzp);
  1455.     now = gmtime(&tp.tv_sec);
  1456.     gmtYear = now->tm_year;
  1457.     gmtDay = now->tm_yday;
  1458.     gmtHour = now->tm_hour;
  1459.     gmtMin = now->tm_min;
  1460.     now = localtime(&tp.tv_sec);
  1461.          if (gmtYear > now->tm_year) GMTDiff = - (60*24);
  1462.     else if (gmtYear < now->tm_year) GMTDiff = (60*24);
  1463.     else if (gmtDay > now->tm_yday) GMTDiff = - (60*24);
  1464.     else if (gmtDay < now->tm_yday) GMTDiff = (60*24);
  1465.     else GMTDiff = 0;
  1466.     GMTDiff += (now->tm_hour - gmtHour) * 60;
  1467.     GMTDiff += (now->tm_min - gmtMin);
  1468.  
  1469.     return GMTDiff;
  1470. }
  1471.  
  1472. static timezone(gmt)
  1473.     register long *gmt;
  1474. {
  1475.     long days;
  1476.     long minutes;
  1477.     long temp = offset();
  1478. /*  struct tm *tm; 
  1479. */
  1480.  
  1481.     if (gmt == 0) return;
  1482.  
  1483.     setrep(&dtm[result], RDAYS);
  1484.  
  1485.     /* Convert to 1/1/70 */
  1486.     days = dtm[result].days;
  1487.     days -= (693960 + 25567);
  1488.  
  1489.     minutes = days*24 + dtm[result].tm.tm_hour;
  1490.     minutes = minutes*60 + dtm[result].tm.tm_min;
  1491.     /* fixed timezone processing!? -- ALD/ISI */
  1492.     minutes -= (zone == -1)? temp: zone;
  1493. /*    minutes += (zone - temp); */
  1494.     *gmt = minutes*60 + dtm[result].tm.tm_sec;
  1495. /*
  1496.     printf("zone = %d, offset = %ld, GMT: %u\n", 
  1497.        zone, temp, *gmt);
  1498.     tm = gmtime(gmt);
  1499.     printf("asctime(gmtime(gmt)) = %s\n", asctime(tm));
  1500.     tm = localtime(gmt);
  1501.     printf("asctime(localtime(gmt)) = %s\n", asctime(tm));
  1502. */
  1503. }
  1504.